﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace DTO
{
    public class Vector
    {
        #region Attributes
        int webpageID;
        List<double> value;
        int clusterNumber; 
        #endregion

        #region Properties
        /// <summary>
        /// The number ID of the cluster this vector belongs to
        /// </summary>
        public int ClusterNumber
        {
            get
            {
                return clusterNumber;
            }
            set
            {
                clusterNumber = value;
            }
        }

        /// <summary>
        /// The webpage id that this vector belongs to.
        /// </summary>
        public int WebpageID
        {
            get
            {
                return webpageID;
            }
            set
            {
                webpageID = value;
            }
        }

        /// <summary>
        /// The t-dimension vector stored in a list of double.
        /// </summary>
        public List<double> Value
        {
            get
            {
                return this.value;
            }
            set
            {
                this.value = value;
            }
        } 
        #endregion

        #region Constructors
        public Vector()
        {
            value = new List<double>();
        }

        public Vector(double[] input)
        {
            value = new List<double>();
            value.AddRange(input);
        } 
        #endregion

        #region Methods
        /// <summary>
        /// Normalize this vector 
        /// </summary>
        public void Normalize()
        {
            double sum = 0;
            foreach (double i in this.value)
            {
                sum += i;
            }
            for (int i = 0; i < this.value.Count; i++)
            {
                this.value[i] = this.value[i] / sum;
            }
        }

        /// <summary>
        /// Export list of vectors to file: webpageID + list of vector values
        /// </summary>
        /// <param name="vectorList">List of vectors</param>
        /// <param name="filePath">The path (with name) of the exported file</param>
        public static void ToVectorFile(List<Vector> vectorList, string filePath)
        {
            FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write);
            StreamWriter sw = new StreamWriter(fs);
            foreach (Vector curVector in vectorList)
            {
                sw.Write(curVector.WebpageID + " ");
                foreach (double i in curVector.Value)
                {
                    sw.Write(i + " ");
                }
                sw.WriteLine();
            }
            sw.Close();
            fs.Close();
        }

        /// <summary>
        /// Import list of vectors from file
        /// </summary>
        /// <param name="filePath">The path (with name) of the imported file</param>
        /// <returns>List of imported vectors</returns>
        public static List<Vector> FromVectorFile(string filePath)
        {
            FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
            StreamReader sr = new StreamReader(fs);
            List<Vector> vectorList = new List<Vector>();

            try
            {
                while (sr.Peek() >= 0)
                {
                    string line = sr.ReadLine();
                    string[] numArr = line.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                    Vector curVector = new Vector();

                    curVector.Value = new List<double>();
                    curVector.WebpageID = Int32.Parse(numArr[0]);
                    for (int i = 1; i < numArr.Length; i++)
                    {
                        curVector.Value.Add(Double.Parse(numArr[i]));
                    }
                    vectorList.Add(curVector);
                }
            }
            finally
            {
                sr.Close();
            }
            sr.Close();
            fs.Close();
            return vectorList;
        }

        /// <summary>
        /// Convert all values in this vector to string
        /// </summary>
        /// <returns>The string of values in this vector</returns>
        public override string ToString()
        {
            string result = string.Empty;
            foreach (double i in this.value)
            {
                decimal temp = new decimal(i);
                result += temp.ToString() + " ";
            }
            return result;
        }

        /// <summary>
        /// Create vector from string
        /// </summary>
        /// <param name="input">The input string contains vector values</param>
        /// <returns>The string of values in this vector</returns>
        public static Vector FromString(string input)
        {
            Vector curVector = new Vector();
            string[] splitStr = input.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
            curVector.Value = new List<double>();
            foreach (string curValue in splitStr)
            {
                curVector.Value.Add(Double.Parse(curValue));
            }
            return curVector;
        }


        /// <summary>
        /// This method get the candidates of a concept through its feature vector. The candidates list will be sorted by theirs scores.
        /// </summary>
        /// <param name="featureVector">The feature vector of a concept</param>
        /// <param name="termList">The list of all the terms used in vectorize phase</param>
        /// <returns>The list of the candidates of this concept</returns>
        public static List<Keyword> FeatureVectorToTerms(Vector featureVector, List<Keyword> termList)
        {
            List<Keyword> sortedTermList = new List<Keyword>(termList);
            for (int i = 0; i < termList.Count; i++)
            {
                sortedTermList[i].Score = featureVector.Value[i];
            }
            sortedTermList.Sort(delegate(Keyword k1, Keyword k2)
            {
                return k2.Score.CompareTo(k1.Score);
            });
            return sortedTermList;
        }

        /// <summary>
        /// This method calculates the cosine between 2 vector a and b.
        /// </summary>
        /// <param name="a">Vector a</param>
        /// <param name="b">Vector a</param>
        /// <returns>The cosine of the angle between 2 vectors</returns>
        public static double Cosine(Vector a, Vector b)
        {
            return (DotProduct(a, b) / (a.Magnitude() * b.Magnitude()));
        }

        /// <summary>
        /// This method calculates the dot product of 2 vector a and b.
        /// </summary>
        /// <param name="a">Vector a</param>
        /// <param name="b">Vector a</param>
        /// <returns>The dot product of 2 vectors</returns>
        public static double DotProduct(Vector a, Vector b)
        {
            double sum = 0.0;
            for (int i = 0; i < a.Value.Count; i++)
            {
                sum += (a.Value[i] * b.Value[i]);
            }
            return sum;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public double Magnitude()
        {
            double sum = 0.0;
            foreach (double i in this.value)
            {
                sum += (i * i);
            }
            return Math.Sqrt(sum);
        } 
        #endregion
    }
}
